﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary.Statistics.Distribution
{
    /// <summary>
    /// This class represents a log normal distribution.
    /// </summary>
    [Serializable]
    public class LogNormalDistribution
    {
        /// <summary>
        /// The scale parameter of the distribution.
        /// </summary>
        private readonly double scale;

        /// <summary>
        /// The location parameter of the distribution.
        /// </summary>
        private readonly double location;

        /// <summary>
        /// Initializes a new instance of the <see cref="LogNormalDistribution"/> class.
        /// </summary>
        /// <param name="scale">The scale parameter of the distribution.</param>
        /// <param name="location">The location parameter of the distribution.</param>
        public LogNormalDistribution(double location, double scale)
        {
            if (scale <= 0)
            {
                throw new ArgumentException("scale <= 0");
            }

            this.location = location;
            this.scale = scale;
        }

        /// <summary>
        /// Gets the scale parameter of the distribution.
        /// </summary>
        /// <value>The scale parameter of the distribution.</value>
        public double Scale
        {
            get { return this.scale; }
        }

        /// <summary>
        /// Gets the location parameter of the distribution.
        /// </summary>
        /// <value>The location parameter of the distribution.</value>
        public double Location
        {
            get { return this.location; }
        }

        /// <summary>
        /// Gets the mean of the distribution.
        /// </summary>
        /// <value>The mean of the distribution.</value>
        public double Mean
        {
            get { return Math.Exp(this.location + (this.scale*this.scale)/2); }
        }

        /// <summary>
        /// Gets the median of the distribution.
        /// </summary>
        /// <value>The median of the distribution.</value>
        public double Median
        {
            get { return Math.Exp(this.location); }
        }

        /// <summary>
        /// Gets the mode of the distribution.
        /// </summary>
        /// <value>The mode of the distribution.</value>
        public double Mode
        {
            get { return Math.Exp(this.location - (this.scale*this.scale)); }
        }

        /// <summary>
        /// Gets the variance of the distribution.
        /// </summary>
        /// <value>The variance of the distribution.</value>
        public double Variance
        {
            get { return (Math.Exp(this.scale*this.scale) - 1)*Math.Exp(2*this.location + this.scale*this.scale); }
        }

        /// <summary>
        /// Gets the skewness of the distribution.
        /// </summary>
        /// <value>The skewness of the distribution.</value>
        public double Skewness
        {
            get { return (Math.Exp(this.scale*this.scale) + 2)*Math.Sqrt(Math.Exp(this.scale*this.scale) - 1); }
        }

        /// <summary>
        /// Computes the probability mass function of the log normal distribution at the specified postion x.
        /// </summary>
        /// <param name="x">The specified position x.</param>
        /// <returns>The probability mass function of the log normal distribution at the specified postion x.</returns>
        public double ProbabilityMassFunction(double x)
        {
            return (1/(x*this.scale*Math.Sqrt(2*Math.PI)))*
                   Math.Exp(-((Math.Pow(Math.Log(x) - this.location, 2))/2*this.scale*this.scale));
        }

        /// <summary>
        /// Computes the cumulative distribution function of the log normal distribution at the specified postion x.
        /// </summary>
        /// <param name="x">The specified position x.</param>
        /// <returns>The cumulative distribution function of the log normal distribution at the specified postion x.</returns>
        public double CumulativeDistributionFunction(double x)
        {
            return 0.5 + 0.5*SpecialFunctions.ErrorFunction((Math.Log(x) - this.location)/(this.scale*Math.Sqrt(2)));
        }
    }
}